package epoch.db.gamedb;

import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import epoch.game.core.cache.ICache;
import epoch.db.gamedb.dialect.Dialect;
import epoch.db.gamedb.dialect.MysqlDialect;
import epoch.db.gamedb.provider.IDataSourceProvider;
import epoch.game.core.util.StrKit;

public class ActiveRecord {

//	private boolean initial = false;
	private String configName;
	private DataSource dataSource;
	private IDataSourceProvider dataSourceProvider;
	private Config config;
	private Dialect dialect;
	private boolean showSql = false;
	private IDBCache cache;
	private int transactionLevel;
	//注册映射的表
	private List<Table> tableList = new ArrayList<Table>();
	
	public void init() {		
		if (configName == null) {
			configName = DbKit.MAIN_CONFIG_NAME;
		}
		if (dataSource == null && dataSourceProvider != null) {
			dataSource = dataSourceProvider.getDataSource();
		}
		if (dataSource == null) {
			throw new RuntimeException("ActiveRecord start error: ActiveRecordPlugin need DataSource or DataSourceProvider");
		}
		if (config == null) {
			config = new Config(configName, dataSource);
		}
		
		if (dialect != null) {
			config.dialect = dialect;
		}
		config.showSql = showSql;
		config.transactionLevel = transactionLevel;
		if (cache != null) {
			config.cache = cache;
		}
		new TableBuilder().build(tableList, config);
		DbKit.addConfig(config);
		Db.init();
//		initial = true;
	}
	
	public ActiveRecord(String configName, DataSource dataSource, int transactionLevel) {
		if (StrKit.isBlank(configName)) {
			throw new IllegalArgumentException("configName can not be blank");
		}
		if (dataSource == null) {
			throw new IllegalArgumentException("dataSource can not be null");
		}
		this.configName = configName.trim();
		this.dataSource = dataSource;
		this.setTransactionLevel(transactionLevel);
	}
	
	public ActiveRecord(DataSource dataSource) {
		this(DbKit.MAIN_CONFIG_NAME, dataSource);
	}
	
	public ActiveRecord(String configName, DataSource dataSource) {
		this(configName, dataSource, DbKit.DEFAULT_TRANSACTION_LEVEL);
	}
	
	public ActiveRecord(DataSource dataSource, int transactionLevel) {
		this(DbKit.MAIN_CONFIG_NAME, dataSource, transactionLevel);
	}
	
	public ActiveRecord(String configName, IDataSourceProvider dataSourceProvider, int transactionLevel) {
		if (StrKit.isBlank(configName)) {
			throw new IllegalArgumentException("configName can not be blank");
		}
		if (dataSourceProvider == null) {
			throw new IllegalArgumentException("dataSourceProvider can not be null");
		}
		this.configName = configName.trim();
		this.dataSourceProvider = dataSourceProvider;
		this.setTransactionLevel(transactionLevel);
	}
	
	public ActiveRecord(IDataSourceProvider dataSourceProvider) {
		this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider);
	}
	
	public ActiveRecord(String configName, IDataSourceProvider dataSourceProvider) {
		this(configName, dataSourceProvider, DbKit.DEFAULT_TRANSACTION_LEVEL);
	}
	
	public ActiveRecord(IDataSourceProvider dataSourceProvider, int transactionLevel) {
		this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider, transactionLevel);
	}
	
	public ActiveRecord(Config config) {
		if (config == null) {
			throw new IllegalArgumentException("Config can not be null");
		}
		this.config = config;
	}
	
	public Config getConfig() {
		return config;
	}

	public ActiveRecord addMapping(String tableName, String primaryKey, Class<? extends Entity> modelClass) {
		tableList.add(new Table(tableName, primaryKey, modelClass));
		return this;
	}
	
	public ActiveRecord addMapping(String tableName, Class<? extends Entity> modelClass) {
		tableList.add(new Table(tableName, modelClass));
		return this;
	}
	
	/**
	 * Set transaction level define in java.sql.Connection
	 * @param transactionLevel only be 0, 1, 2, 4, 8
	 */
	public ActiveRecord setTransactionLevel(int transactionLevel) {
		int t = transactionLevel;
		if (t != 0 && t != 1  && t != 2  && t != 4  && t != 8) {
			throw new IllegalArgumentException("The transactionLevel only be 0, 1, 2, 4, 8");
		}
		this.transactionLevel = transactionLevel;
		return this;
	}
	
	public ActiveRecord setCache(IDBCache cache) {
		if (cache == null) {
			throw new IllegalArgumentException("cache can not be null");
		}
		this.cache = cache;
		return this;
	}
	
	public ActiveRecord setShowSql(boolean showSql) {
		this.showSql = showSql;
		return this;
	}

	
	public ActiveRecord setDialect(Dialect dialect) {
		if (dialect == null) {
			throw new IllegalArgumentException("dialect can not be null");
		}
		this.dialect = dialect;
		return this;
	}

	
	/**
	 * 当使用 create table 语句创建用于开发使用的数据表副本时，假如create table 中使用的
	 * 复合主键次序不同，那么MappingKitGeneretor 反射生成的复合主键次序也会不同。
	 * 
	 * 而程序中类似于 model.deleteById(id1, id2) 方法中复合主键次序与需要与映射时的次序
	 * 保持一致，可以在MappingKit 映射完成以后通过调用此方法再次强制指定复合主键次序
	 * 
	 * <pre>
	 * Example:
	 * ActiveRecrodPlugin arp = new ActiveRecordPlugin(...);
	 * _MappingKit.mapping(arp);
	 * arp.setPrimaryKey("account_role", "account_id, role_id");
	 * me.add(arp);
	 * </pre>
	 */
	public void setPrimaryKey(String tableName, String primaryKey) {
		for (Table table : tableList) {
			if (table.getName().equalsIgnoreCase(tableName.trim())) {
				table.setPrimaryKey(primaryKey);
			}
		}
	}
	
	
	
	/**
	 * 用于分布式场景，当某个分布式节点只需要用 Model 承载和传输数据，而不需要实际操作数据库时
	 * 调用本方法可保障 IContainerFactory、Dialect、ICache 的一致性
	 * 
	 * 本用法更加适用于 Generator 生成的继承自 base model的 Model，更加便于传统第三方工具对
	 * 带有 getter、setter 的 model 进行各种处理
	 * 
	 * <pre>
	 * 警告：Dialect、IContainerFactory、ICache 三者一定要与集群中其它节点保持一致，
	 *     以免程序出现不一致行为
	 * </pre>
	 */
	public static void useAsDataTransfer(Dialect dialect, IDBCache cache) {
//		if (dialect == null) {
//			throw new IllegalArgumentException("dialect can not be null");
//		}
//		
//		if (cache == null) {
//			throw new IllegalArgumentException("cache can not be null");
//		}
//		ActiveRecord ar = new ActiveRecord(new NullDataSource());
//		ar.setDialect(dialect);
//		ar.setCache(cache);
//		ar.init();
//		DbKit.brokenConfig = ar.config;
	}
	

	/**
	 * 分布式场景下指定 Dialect、 并默认使用 IContainerFactory.defaultContainerFactory、EhCache
	 * @see #useAsDataTransfer(Dialect, IContainerFactory, ICache)
	 */
	public static void useAsDataTransfer(Dialect dialect) {
		useAsDataTransfer(dialect, new DBCache());
	}
	
	/**
	 * 分布式场景下默认使用 MysqlDialect、 IContainerFactory.defaultContainerFactory、EhCache
	 * @see #useAsDataTransfer(Dialect, IContainerFactory, ICache)
	 */
	public static void useAsDataTransfer() {
		useAsDataTransfer(new MysqlDialect(), new DBCache());
	}
	
}
